package engine

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"sync"
	"time"
)

// HealthChecker 健康检查器
type HealthChecker struct {
	backends  []*Backend
	interval  time.Duration
	timeout   time.Duration
	stopCh    chan struct{}
	checkPath string
	mu        sync.RWMutex
}

func NewHealthChecker(backends []*Backend, interval, timeout time.Duration, checkPath string) *HealthChecker {
	return &HealthChecker{
		backends:  backends,
		interval:  interval,
		timeout:   timeout,
		stopCh:    make(chan struct{}),
		checkPath: checkPath,
	}
}

func (h *HealthChecker) Start() {
	ticker := time.NewTicker(h.interval)
	go func() {
		for {
			select {
			case <-ticker.C:
				h.checkAll()
			case <-h.stopCh:
				ticker.Stop()
				return
			}
		}
	}()
}

func (h *HealthChecker) Stop() {
	close(h.stopCh)
}

func (h *HealthChecker) checkAll() {
	h.mu.RLock()
	backends := make([]*Backend, len(h.backends))
	copy(backends, h.backends)
	h.mu.RUnlock()

	var wg sync.WaitGroup
	for _, b := range backends {
		wg.Add(1)
		go func(backend *Backend) {
			defer wg.Done()
			isHealthy := h.checkBackend(backend)
			backend.Health.Store(isHealthy)
			if !isHealthy {
				backend.FailCount.Add(1)
			} else {
				backend.FailCount.Store(0)
			}
		}(b)
	}
	wg.Wait()
}

func (h *HealthChecker) checkBackend(backend *Backend) bool {
	u, err := url.Parse(backend.URL)
	if err != nil {
		return false
	}

	healthURL := fmt.Sprintf("%s%s", u.String(), h.checkPath)
	ctx, cancel := context.WithTimeout(context.Background(), h.timeout)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, "GET", healthURL, nil)
	if err != nil {
		return false
	}

	client := &http.Client{
		Timeout: h.timeout,
	}

	resp, err := client.Do(req)
	if err != nil {
		return false
	}
	defer resp.Body.Close()

	return resp.StatusCode == http.StatusOK
}

func (h *HealthChecker) UpdateBackends(backends []*Backend) {
	h.mu.Lock()
	h.backends = backends
	h.mu.Unlock()
}

func (h *HealthChecker) GetHealthyBackends() []*Backend {
	h.mu.RLock()
	defer h.mu.RUnlock()

	healthy := make([]*Backend, 0)
	for _, backend := range h.backends {
		if backend.Health.Load() {
			healthy = append(healthy, backend)
		}
	}
	return healthy
}
